package mysql

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"strings"
	"xrt/constants"
	"xrt/core"
	"xrt/interfaces"
	"xrt/utils"
	"xrt/utils/file/log"
	"xrt/utils/maps"
)

type mysqlLayer struct {
	db *sql.DB
}

func (m *mysqlLayer) Connect(mysqlConfigs map[string]string) {
	connectString := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s",
		mysqlConfigs["username"], mysqlConfigs["password"], mysqlConfigs["host"], mysqlConfigs["port"],
		mysqlConfigs["database"], mysqlConfigs["format"])
	if core.IsDebug() {
		log.Debugf("Initial MysqlLayer: [%s]\n", connectString)
	}
	db, err := sql.Open("mysql", connectString)
	if utils.CheckErr(err) {
		m.db = db
		m.db.SetMaxOpenConns(utils.ParseInt(maps.GetKeyWithDefaultS(mysqlConfigs, "maxConnection", "0")))
		m.db.SetMaxIdleConns(utils.ParseInt(maps.GetKeyWithDefaultS(mysqlConfigs, "maxIdleConnection", "10")))
		m.Test()
	}
	recover()
}

func (m *mysqlLayer) Test() {
	m.Query("select 1 from dual;")
}

func (m *mysqlLayer) Query(sql string) []map[string]string {
	log.Debugf("Execute Sql: %s", sql)
	var resultData []map[string]string
	if rows, err := m.db.Query(sql); err == nil {
		column, _ := rows.Columns()
		values := make([][]byte, len(column))
		scans := make([]interface{}, len(column))
		for i := range values {
			scans[i] = &values[i]
		}
		for rows.Next() {
			if err := rows.Scan(scans...); err != nil {
				log.Error(err)
				break
			}
			row := make(map[string]string)
			for k, v := range values {
				key := column[k]
				row[strings.ToLower(key)] = string(v)
			}
			resultData = append(resultData, row)
		}
	} else {
		log.Error(err)
	}
	return resultData
}

func (m *mysqlLayer) GetOne(sql string) map[string]string {
	resData := m.Query(sql)
	if len(resData) > 0 {
		return resData[0]
	} else {
		return map[string]string{}
	}
}

func (m *mysqlLayer) Execute(sql string) error {
	log.Debugf("Execute Sql: %s", sql)
	result, err := m.db.Exec(sql)
	if utils.CheckErr(err) {
		result.RowsAffected()
	}
	return err
}

func (m *mysqlLayer) Select(tableName string, params ...interface{}) []map[string]string {
	return nil
}
func (m *mysqlLayer) Insert(tableName string, params ...interface{}) bool { return false }
func (m *mysqlLayer) Update(tableName string, params ...interface{}) bool { return false }
func (m *mysqlLayer) Delete(tableName string, params ...interface{}) bool { return false }

func (m *mysqlLayer) Table(tableName string) interfaces.OrmModel {
	return interfaces.OrmModel{SqlConn: m, Table: tableName}
}

func init() {
	core.X.RegisterComponent(constants.ImplSql, mysqlLayer{})
}
